package com.fjec.common.okhttp;

import com.fjec.common.util.Base64Util;
import com.fjec.common.util.BizUtil;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import okhttp3.internal.Util;
import okhttp3.logging.HttpLoggingInterceptor;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.ssl.SSLContexts;

import javax.activation.MimetypesFileTypeMap;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.security.KeyStore;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * Http工具类
 */
@Slf4j
public class OkHttp {

    private OkHttpClient client;

    private MimetypesFileTypeMap typeMap = new MimetypesFileTypeMap();

    private Integer connectTimeout = 10;

    private Integer writeTimeout = 20;

    private Integer readTimeout = 20;

    private boolean retryOnConnectionFailure = true;

    public OkHttp() {

    }

    public OkHttp(Integer connectTimeout,
                  Integer writeTimeout,
                  Integer readTimeout,
                  boolean retryOnConnectionFailure) {
        this.connectTimeout = connectTimeout;
        this.writeTimeout = writeTimeout;
        this.readTimeout = readTimeout;
        this.retryOnConnectionFailure = retryOnConnectionFailure;
    }

    /**
     * 初始化OkHttp
     */
    public void init() {
        //log
        HttpLoggingInterceptor logInterceptor = new HttpLoggingInterceptor(new HttpLogger());
        logInterceptor.setLevel(HttpLoggingInterceptor.Level.HEADERS);
        //herader
        HeaderInterceptor headerInterceptor = new HeaderInterceptor();
        //build
        OkHttpClient.Builder builder = new OkHttpClient.Builder();
        builder.hostnameVerifier(new MyHostnameVerifier())
                .retryOnConnectionFailure(true)
                .connectTimeout(connectTimeout, TimeUnit.SECONDS)
                .writeTimeout(writeTimeout, TimeUnit.SECONDS)
                .readTimeout(readTimeout, TimeUnit.SECONDS)
                .addInterceptor(headerInterceptor)
                .retryOnConnectionFailure(retryOnConnectionFailure)
                .addNetworkInterceptor(logInterceptor);

        //init
        client = builder.build();
    }

    /**
     * 初始化OkHttp，使用证书
     *
     * @param certBytes 证书二进制数组
     * @param pwd 密码
     * @throws Exception
     */
    public void initWithCert(byte[] certBytes, String pwd) throws Exception {
        //log
        HttpLoggingInterceptor logInterceptor = new HttpLoggingInterceptor(new HttpLogger());
        logInterceptor.setLevel(HttpLoggingInterceptor.Level.HEADERS);
        //herader
        HeaderInterceptor headerInterceptor = new HeaderInterceptor();
        //build
        OkHttpClient.Builder builder = new OkHttpClient.Builder();
        builder.sslSocketFactory(sslSocketFactory(certBytes, pwd), new MyX509TrustManager())
                .hostnameVerifier(new MyHostnameVerifier())
                .retryOnConnectionFailure(true)
                .connectTimeout(connectTimeout, TimeUnit.SECONDS)
                .writeTimeout(writeTimeout, TimeUnit.SECONDS)
                .readTimeout(readTimeout, TimeUnit.SECONDS)
                .addInterceptor(headerInterceptor)
                .retryOnConnectionFailure(retryOnConnectionFailure)
                .addNetworkInterceptor(logInterceptor);
        //init
        client = builder.build();
    }

    /**
     * 装载证书
     *
     * @return
     */
    private SSLSocketFactory sslSocketFactory(byte[] certBytes, String pwd) throws Exception {
        // 装载证书
        SSLSocketFactory sslSocketFactory = null;
        try {
            // 创建SSLSocketFactory
            SSLContextBuilder sslBuilder = SSLContexts.custom();
            // 加载证书
            ByteArrayInputStream fis = new ByteArrayInputStream(certBytes);
            KeyStore ks = KeyStore.getInstance("pkcs12");
            ks.load(fis, pwd.toCharArray());
            Util.closeQuietly(fis);
            sslBuilder.loadKeyMaterial(ks, pwd.toCharArray());
            SSLContext sslcontext = sslBuilder.build();
            sslSocketFactory = sslcontext.getSocketFactory();
        } catch (Exception e) {
            log.error("customSSLSocketFactory.error", e);
            throw e;
        }
        return sslSocketFactory;
    }

    /**
     * 同步调用
     *
     * @param request  请求参数
     * @param callBack 回调
     */
    public <T> T doRequest(Request request, HttpCallBack<T> callBack) throws IOException {
        Response response = null;
        try {
            response = client.newCall(request).execute();
            return callBack.process(response);
        } finally {
            Util.closeQuietly(response);
        }
    }

    public byte[] postJsonBytes(String url, String jsonString) {
        MediaType mediaType = MediaType.parse("application/json; charset=utf-8");
        RequestBody body = RequestBody.create(mediaType, jsonString);
        Request request = new Request.Builder().post(body).url(url).build();
        try {
            Response response = client.newCall(request).execute();
            if (response.isSuccessful()) {
                return response.body().bytes();
            } else {
                throw new RuntimeException("Unexpected code " + response);
            }
        } catch (IOException e) {
            log.error("postJson.error", e);
        }
        return null;

    }

    /**
     * Post提交(application-json)方式
     *
     * @param url        提交地址
     * @param jsonString json内容
     */
    public String postJson(String url, String jsonString) {
        MediaType mediaType = MediaType.parse("application/json; charset=utf-8");
        RequestBody body = RequestBody.create(mediaType, jsonString);
        Request request = new Request.Builder().post(body).url(url).build();

        try {
            return doRequest(request, response -> new String(response.body().bytes(), "utf-8"));
        } catch (IOException e) {
            log.error("postJson.error", e);
        }
        return null;
    }

    /**
     * Post提交(form-data)方式
     *
     * @param url    提交地址
     * @param params 键值对
     */
    public String postMap(String url, Map<String, String> params) {
        FormBody.Builder builder = new FormBody.Builder();
        params.forEach((key, value) -> {
            builder.add(key, value);
        });
        Request request = new Request.Builder().post(builder.build()).url(url).build();

        try {
            return doRequest(request, new HttpCallBack<String>() {
                @Override
                public String process(Response response) throws IOException {
                    return new String(response.body().bytes(), "utf-8");
                }
            });
        } catch (IOException e) {
            log.error("postMap.error", e);
        }
        return null;
    }

    /**
     * Post提交(multipart/form-data)方式
     *
     * @param url    提交地址
     * @param params 键值对
     */
    public String postFile(String url, Map<String, Object> params) throws Exception {
        // 构建请求数据
        MultipartBody.Builder builder = new MultipartBody.Builder();
        builder.setType(MultipartBody.FORM);
        params.forEach((key, value) -> {
            if (value instanceof File) {
                File file = (File) value;
                builder.addFormDataPart(key, file.getName(), RequestBody.create(getMediaType(file), file));
            } else {
                builder.addFormDataPart(key, (String) value);
            }
        });
        Request request = new Request.Builder().post(builder.build()).url(url).build();
        return doRequest(request, new HttpCallBack<String>() {
            @Override
            public String process(Response response) throws IOException {
                return new String(response.body().bytes(), "utf-8");
            }
        });
    }

    /**
     * Post提交(multipart/form-data)方式
     *
     * @param url    提交地址
     * @param params 键值对
     */
    public String postBytes(String url, Map<String, Object> params) throws Exception {
        // 构建请求数据
        MultipartBody.Builder builder = new MultipartBody.Builder();
        builder.setType(MultipartBody.FORM);
        params.forEach((key, value) -> {
            if (value instanceof byte[]) {
                byte[] bytes = (byte[]) value;
                builder.addFormDataPart(key, BizUtil.guid() + ".png", RequestBody.create(null, bytes));
            } else {
                builder.addFormDataPart(key, (String) value);
            }
        });
        Request request = new Request.Builder().post(builder.build()).url(url).build();
        return doRequest(request, new HttpCallBack<String>() {
            @Override
            public String process(Response response) throws IOException {
                return new String(response.body().bytes(), "utf-8");
            }
        });
    }

    /**
     * 异步调用
     *
     * @param request
     * @param callback
     */
    public void enqueue(Request request, Callback callback) {
        client.newCall(request).enqueue(callback);
    }

    /**
     * 获取MIME类型
     *
     * @param fileName 文件名称 如 readme.txt 则返回text/plain
     */
    public MediaType getMediaType(String fileName) {
        return MediaType.parse(typeMap.getContentType(fileName));
    }

    /**
     * 获取MIME类型
     *
     * @param file 文件 如 readme.txt 则返回text/plain
     */
    public MediaType getMediaType(File file) {
        return MediaType.parse(typeMap.getContentType(file));
    }

    public String get(String url) {
        try {
            Request request = new Request.Builder().get().url(url).build();
            Response response = client.newCall(request).execute();
            if (response.isSuccessful()) {

                return response.body().string();

            } else {
                throw new RuntimeException("Unexpected code " + response);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Post提交(form-data)方式
     *
     * @param url    提交地址
     * @param params 键值对
     */
    public String postMapObject(String url, Map<String, Object> params) {
        FormBody.Builder builder = new FormBody.Builder();
        params.forEach((key, value) -> {
            builder.add(key, value.toString());
        });
        Request request = new Request.Builder().post(builder.build()).url(url).build();

        try {
            return doRequest(request, new HttpCallBack<String>() {
                @Override
                public String process(Response response) throws IOException {
                    return new String(response.body().bytes(), "utf-8");
                }
            });
        } catch (IOException e) {
            log.error("postMap.error", e);
        }
        return null;
    }

    /**
     * 从图片链接中转换流写入Response
     *
     * @param url
     * @param res
     * @throws Exception
     */
    public void convertUrl2Img(String url, HttpServletResponse res) throws Exception {
        Request request = new Request.Builder().url(url).build();
        Response response = client.newCall(request).execute();
        InputStream is = response.body().byteStream();
        OutputStream out = res.getOutputStream();
        int len;
        byte[] bytes = new byte[4096];
        while ((len = is.read(bytes)) != -1) {
            out.write(bytes, 0, len);
        }
        out.flush();
    }

    /**
     * 从图片链接中转换为Base64字符串
     *
     * @param url
     * @throws Exception
     */
    public String convertUrl2Base64(String url) throws Exception {
        Request request = new Request.Builder().url(url).build();
        Response response = client.newCall(request).execute();
        InputStream is = response.body().byteStream();
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        int len;
        byte[] bytes = new byte[4096];
        while ((len = is.read(bytes)) != -1) {
            baos.write(bytes, 0, len);
        }
        return Base64Util.encode(baos.toByteArray());
    }

}
